package com.herch.system.service.impl;

import com.herch.common.constant.MnlConstans;
import com.herch.common.utils.CommonUtil;
import com.herch.system.domain.vo.SerialPortVo;
import com.herch.system.service.IFZ420DataAcquisitionService;
import com.herch.system.service.IRS485DataAcquisitionService;
import com.herch.system.service.SerialPortService;
import com.herch.system.task.SerialPortTask;
import gnu.io.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.server.PortInUseException;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;

/**
 * @Author: fei.li02
 * @Date: 2023/7/10 16:59
 * @Description:串口通讯服务类
 */
@Service
public class SerialPortServiceImpl implements SerialPortService {
    private static final Logger log = LoggerFactory.getLogger(SerialPortServiceImpl.class);
    public static SerialPort rs485MSerialport = null;
    public static SerialPort fz420MSerialport = null;
    @Autowired
    private IRS485DataAcquisitionService dataRS485AcquisitionService;
    @Autowired
    private IFZ420DataAcquisitionService dataFZ420AcquisitionService;
    @Override
    public ArrayList<String> findPorts() {
        // 获得当前所有可用串口
        Enumeration<CommPortIdentifier> portList = CommPortIdentifier.getPortIdentifiers();
        ArrayList<String> portNameList = new ArrayList<String>();
        // 将可用串口名添加到List并返回该List
        while (portList.hasMoreElements()) {
            String portName = portList.nextElement().getName();
            portNameList.add(portName);
        }
        return portNameList;
    }

    @Override
    public Map<String, Object> openPort(SerialPortVo serialPortVo) {
        Map<String, Object> resultMap = new HashMap<String, Object>();
        if (rs485MSerialport != null) {
            resultMap.put("message", MnlConstans.serialPort_message_RS485_1);
            resultMap.put("result", false);
            return resultMap;
        }
        if (fz420MSerialport != null) {
            resultMap.put("message", MnlConstans.serialPort_message_FZ420_1);
            resultMap.put("result", false);
            return resultMap;
        }
        String rs485CommName = serialPortVo.getRs485SerialPort();
        String fz420CommName = serialPortVo.getFz420SerialPort();
        int rs485Baudrate = Integer.valueOf(serialPortVo.getRs485Baudrate());
        int fz420Baudrate = Integer.valueOf(serialPortVo.getFz420Baudrate());
        int dataBits = Integer.valueOf(serialPortVo.getDataBits());
        int parity = Integer.valueOf(serialPortVo.getParity());
        int stopBits = Integer.valueOf(serialPortVo.getStopBits());
        try {
            rs485MSerialport = openPort(rs485CommName, rs485Baudrate, dataBits, parity, stopBits);
            fz420MSerialport = openPort(fz420CommName, fz420Baudrate, dataBits, parity, stopBits);
            boolean rs485OpenFlag = false;
            boolean fz420OpenFlag = false;
            if (rs485MSerialport != null) {
                //解析数据
                rs485OpenFlag = true;
            } else {
                resultMap.put("message", MnlConstans.serialPort_message_RS485_3);
                resultMap.put("result", false);
                return resultMap;
            }
            if (fz420MSerialport != null) {
                fz420OpenFlag = true;
            } else {
                resultMap.put("message", MnlConstans.serialPort_message_FZ420_3);
                resultMap.put("result", false);
                return resultMap;
            }
            if(rs485OpenFlag && fz420OpenFlag){
                resultMap.put("message", MnlConstans.serialPort_message_2);
                resultMap.put("result", true);
            }
            // 添加串口监听
            addListener(rs485MSerialport, new DataAvailableListener() {
                public void dataAvailable() {
                    if (rs485MSerialport == null) {
                        resultMap.put("message", MnlConstans.serialPort_message_RS485_4);
                        resultMap.put("result", false);
                    } else {
                        // 读取串口数据
                        readFromPort(rs485MSerialport,"RS485");
                    }
                }
            });
            // 添加串口监听
            addListener(fz420MSerialport, new DataAvailableListener() {
                public void dataAvailable() {
                    if (fz420MSerialport == null) {
                        resultMap.put("message", MnlConstans.serialPort_message_FZ420_4);
                        resultMap.put("result", false);
                    } else {
                        // 读取串口数据
                        readFromPort(fz420MSerialport,"FZ420");
                    }
                }
            });
            SerialPortTask.isRunning = true;
        } catch (PortInUseException e) {
            resultMap.put("message", MnlConstans.serialPort_message_5);
            resultMap.put("result", false);
            return resultMap;
        }
        return resultMap;
    }

    /**
     * 打开串口
     *
     * @param portName 端口名称
     * @param baudrate 波特率
     * @param stopBits
     * @param parity
     * @param dataBits
     * @return 串口对象
     * @throws PortInUseException 串口已被占用
     */
    public SerialPort openPort(String portName, int baudrate, int dataBits, int parity, int stopBits)
            throws PortInUseException {
        try {
            // 通过端口名识别端口
            CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(portName);
            // 打开端口，并给端口名字和一个timeout（打开操作的超时时间）
            CommPort commPort = portIdentifier.open(portName, 2000);
            // 判断是不是串口
            if (commPort instanceof SerialPort) {
                SerialPort serialPort = (SerialPort) commPort;
                try {
                    // 设置一下串口的波特率等参数
                    serialPort.setSerialPortParams(baudrate, dataBits, stopBits, parity);
                } catch (UnsupportedCommOperationException e) {
                    log.error("SerialPortService openPort Filed:",e);
                }
                return serialPort;
            }
        } catch (NoSuchPortException e) {
            log.error("SerialPortService openPort NoSuchPortException:",e);
        } catch (gnu.io.PortInUseException e) {
            log.error("SerialPortService openPort PortInUseException:",e);
        }
        return null;
    }
    /**
     * 添加监听器
     *
     * @param serialPort 串口对象
     * @param listener 串口存在有效数据监听
     */
    public void addListener(SerialPort serialPort, DataAvailableListener listener) {
        try {
            // 给串口添加监听器
            serialPort.addEventListener(new SerialPortListener(listener));
            // 设置当有数据到达时唤醒监听接收线程
            serialPort.notifyOnDataAvailable(true);
            // 设置当通信中断时唤醒中断线程
            serialPort.notifyOnBreakInterrupt(true);
        } catch (TooManyListenersException e) {
            log.error("SerialPortService addListener:",e);
        }
    }
    /**
     * 串口存在有效数据监听
     */
    public interface DataAvailableListener {
        /**
         * 串口存在有效数据
         */
        void dataAvailable();
    }
    /**
     * 串口监听
     */
    public class SerialPortListener implements SerialPortEventListener {

        private DataAvailableListener mDataAvailableListener;

        public SerialPortListener(DataAvailableListener mDataAvailableListener) {
            this.mDataAvailableListener = mDataAvailableListener;
        }
        public void serialEvent(SerialPortEvent serialPortEvent) {
            switch (serialPortEvent.getEventType()) {
                case SerialPortEvent.DATA_AVAILABLE: // 1.串口存在有效数据
                    if (mDataAvailableListener != null) {
                        mDataAvailableListener.dataAvailable();
                    }
                    break;

                case SerialPortEvent.OUTPUT_BUFFER_EMPTY: // 2.输出缓冲区已清空
                    break;

                case SerialPortEvent.CTS: // 3.清除待发送数据
                    break;

                case SerialPortEvent.DSR: // 4.待发送数据准备好了
                    break;

                case SerialPortEvent.RI: // 5.振铃指示
                    break;

                case SerialPortEvent.CD: // 6.载波检测
                    break;

                case SerialPortEvent.OE: // 7.溢位（溢出）错误
                    break;

                case SerialPortEvent.PE: // 8.奇偶校验错误
                    break;

                case SerialPortEvent.FE: // 9.帧错误
                    break;

                case SerialPortEvent.BI: // 10.通讯中断
                    log.info("Interrupted communication with serial device");
                    break;

                default:
                    break;
            }
        }
    }
    /**
     * 从串口读取数据
     *
     * @param serialPort 当前已建立连接的SerialPort对象
     * @return
     * @return 读取到的数据
     */
    public void readFromPort(SerialPort serialPort,String serialType) {
        String src;
        try {
            InputStream inputStream = null;
            Thread.sleep(300);
            inputStream = serialPort.getInputStream();
            // 通过输入流对象的available方法获取数组字节长度
            byte[] bytes = new byte[inputStream.available()];
            // 从线路上读取数据流
            while ((inputStream.read(bytes)) != -1) {
                // 直接获取到的数据
                src = CommonUtil.bytesToHexString(bytes);
                String inputString = src;
                src = "";
                //解析数据
                if(MnlConstans.RS485.equals(serialType)) {
                    dataRS485AcquisitionService.parseRS485ReceivedData(inputString);
                }else if(MnlConstans.FZ420.equals(serialType)){
                    dataFZ420AcquisitionService.parseFZ420ReceivedData(inputString);
                }
                inputStream = null;
            }
        } catch (IOException e ) {
            log.error("SerialPortService readFromPort IOException:"+e);
      } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public Map<String, Object> closePort() {
        Map<String, Object> resultMap = new HashMap<String, Object>();
        boolean rs485CloseFlag = false;
        boolean fz420CloseFlag = false;
        if (rs485MSerialport != null) {
            rs485MSerialport.close();
            rs485CloseFlag = true;
        }else{
            resultMap.put("message",MnlConstans.serialPort_message_RS485_7);
        }
        if (fz420MSerialport != null) {
            fz420MSerialport.close();
            fz420CloseFlag = true;
        }else{
            resultMap.put("message",MnlConstans.serialPort_message_FZ420_7);
        }
       if(rs485CloseFlag && fz420CloseFlag){
           resultMap.put("message",MnlConstans.serialPort_message_6);
       }
        SerialPortTask.isRunning = false;
        rs485MSerialport = null;
        fz420MSerialport = null;
        return resultMap;
    }
}
